/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.editor.model;

import java.util.Stack;

import javax.swing.JEditorPane;
import javax.swing.JTree;
import javax.swing.event.DocumentEvent;
import javax.swing.text.Element;
import javax.swing.text.html.HTML;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import offset.nodes.client.editor.Editor;
import offset.nodes.client.editor.view.Heading;
import offset.nodes.client.editor.view.HeadingRenderer;
import offset.nodes.client.editor.view.UserCaretListener;

public class HeadingTree extends ElementTree implements UserCaretListener {

    public HeadingTree(JEditorPane pane, JTree tree, Editor editor) {
        super(pane, tree, editor);
        tree.setCellRenderer(new HeadingRenderer());
    }

    public void userSetCaret() {
        int pos = pane.getCaretPosition();
        ParagraphModel model = new ParagraphModel(new DocumentContext(pane));
        Element element = model.getRealParagraph(pos);
        Element heading = model.findHeading(element);
        
        selectElement(heading);
    }

    protected DefaultMutableTreeNode findNode(DefaultMutableTreeNode root, Element element) {
        if (root.getUserObject() instanceof Heading && 
                ((Heading)root.getUserObject()).getElement() == element) {
            return root;
        }

        for (int i = 0; i < root.getChildCount(); i++) {
            DefaultMutableTreeNode node = findNode((DefaultMutableTreeNode) root.getChildAt(i), element);
            if (node != null) {
                return node;
            }
        }

        return null;
    }

    public DefaultMutableTreeNode createTree() {
        Element html = pane.getDocument().getRootElements()[0];
        Element body = null;
        for (int i = 0; i < html.getElementCount(); i++) {
            if (html.getElement(i).getName().equals("body")) {
                body = html.getElement(i);
                break;
            }
        }
        DefaultMutableTreeNode root = createPopupNode("");

        Stack path = new Stack();
        path.push(root);

        createTree(path, body);

        return root;
    }

    public void createTree(Stack path, Element body) {
        for (int i = 0; i < body.getElementCount(); i++) {
            Element e = body.getElement(i);
            if (e.getName().equals(HTML.Tag.DIV.toString())) {
                createTree(path, e);
            }

            if (!e.getName().startsWith("h")) {
                continue;
            }

            int level = getLevel(e);
            while (getLevel((DefaultMutableTreeNode) path.peek()) >= level) {
                path.pop();
            }

            DefaultMutableTreeNode parent = (DefaultMutableTreeNode) path.peek();
            DefaultMutableTreeNode child = createPopupNode(new Heading(e));

            parent.add(child);
            path.push(child);
        }
    }

    protected int getLevel(DefaultMutableTreeNode node) {
        if (node.getUserObject() instanceof Element) {
            Element heading = ((Element) node.getUserObject());
            return getLevel(heading);
        }

        return 0;
    }

    protected int getLevel(Element e) {
        return Integer.parseInt(e.getName().substring(1));
    }

    protected void updateTree(DocumentEvent.EventType type, DocumentEvent e) {
        if (e.getLength() == 1 && !((EditorDocument) e.getDocument()).getParagraphElement(e.getOffset()).getName().startsWith("h")) {
            return;
        }

        DefaultMutableTreeNode root = (DefaultMutableTreeNode) ((DefaultTreeModel) tree.getModel()).getRoot();
        DefaultMutableTreeNode newRoot = createTree();
        updateTree(type, root, newRoot);
    }

    public void changedUpdate(DocumentEvent e) {
        updateTree(e.getType(), e);
    }

    public void insertUpdate(DocumentEvent e) {
        updateTree(e.getType(), e);
    }

    public void removeUpdate(DocumentEvent e) {
        updateTree(e.getType(), e);
    }
}
